/****************************************************************
 ** Product  :   HP Subscriber Network Application Policy
 ** Module   :   gr-manager
 ** Date: Apr 10, 2015               
 ** Author: huxiaoji
 ** (C) Copyright 2015, Hewlett-Packard Company, All Rights Reserved.
 ** This software is the proprietary information of HP, Inc.  
 ** Use is subject to license terms.
 *****************************************************************
 ** Description:   
 **  VoltDB controller encapsulated some VoltDB operations, include start/stop/delete/recover/resume voltdb databases.
 **  
 ****************************************************************/
package com.hp.snap.gr.utils;

import java.util.HashMap;
import java.util.Map;

import com.hp.snap.gr.exception.ActionException;
import com.hp.snap.gr.exception.DatabaseException;
import com.hp.snap.gr.exception.RESTfulException;
import com.hp.snap.gr.model.Database;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * VoltDB controller encapsulated some VoltDB operations, include start/stop/delete/recover/resume voltdb databases. 
 *     Invoke definition restful interface to do these operations.
 *
 * <P>
 * Version: 3.3 <br>
 * Author: huxiaoji
 * Date: Apr 10, 2015
 * 
 * </P>
 *
 **/
public class VoltDBController {
    private static Logger logger = LoggerFactory.getLogger(VoltDBController.class);

    public static void startDatabase(String vemHost, int vemPort, String vemUri, Database database, boolean replica) throws ActionException {
        if (logger.isDebugEnabled()) {
            logger.debug("going to start database {} through http://{}:{}{}",
                    new Object[]{database.getName(), vemHost, vemPort, vemUri});
        }
        if (!Constants.VDB_STATUS_OFFLINE.equals(database.getStatus())) {
            logger.error("database {} is in wrong status {}", database.getName(), database.getStatus());
            throw new DatabaseException("Illegal database status");
        }

        if (!vemUri.endsWith("/")) {
            vemUri = vemUri + "/";
        }

        if (!replica) {
            RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "mgmt/databases/" + database.getId()
                    + "/start", Constants.HTTP_METHOD_PUT, null);
        } else {
            RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "mgmt/databases/" + database.getId()
                    + "/startreplica", Constants.HTTP_METHOD_PUT, null);
        }
        //try to monitor database status

        long start = System.currentTimeMillis();
        long timeout = 600 * 1000;
        String timeoutStr = GRMConfigManager.getLocalConfigValue(Constants.CFG_GRL_VOLT_DB_START_TIMEOUT);
        if (StringUtils.isNotEmpty(timeoutStr)) {
            timeout = Integer.parseInt(timeoutStr) * 1000L;
        }
        if (logger.isTraceEnabled()) {
            logger.trace("waiting for voltdb starting for {} ms", timeout);
        }
        while (true) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
                break;
            }
            Database db = RESTfulUtils.getVoltDBDatabase(database.getName(), false/*not calling stored procedure to check replica status*/);
            if (Constants.VDB_STATUS_ONLINE.equals(db.getStatus())) {
                logger.debug("database {} has been started", db.getName());
                break;
            }
            if (System.currentTimeMillis() - start >= timeout) {
                logger.error("waiting for voltdb database {} starting for {} ms", db.getName(), System.currentTimeMillis() - start);
                throw new ActionException("waiting for voltdb database starting expired");
            }
        }
    }

    public static void startDatabase(Database database, boolean replica) throws ActionException {
        String voltDBNodePath = Constants.CFG_GRG_ROOT + "/" + GRMConfigManager.getSiteName() + "/" + Constants.CFG_GRG_VOLTDB;
        if(logger.isDebugEnabled()){
            logger.debug("The voltDB node path is:{}", voltDBNodePath);
        }
        Map<String, String> vemInfo = new HashMap<String, String>();
        try {
            vemInfo = RESTfulUtils.getVemInfo();
        } catch (ActionException e) {
            throw new RESTfulException(e.getMessage(), e);
        }
        String vemHost = vemInfo.get("vemHost"); //Utils.getVEMHostIP(GRMConfigManager.getSiteName());
        String vemPort = vemInfo.get("vemPort"); //Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_PORT);
        //String vemRootURI = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_URI);
        String vemRootURI = Utils.getConfigurationValue(Constants.CFG_GRG_VEM_URI);
        if(logger.isDebugEnabled()){
            logger.debug("The vemHost is {}", vemHost);
            logger.debug("The vem port is {}", vemPort);
            logger.debug("The vem root URI is {}", vemRootURI);
        }
        if (!vemRootURI.endsWith("/")) {
            vemRootURI = vemRootURI + "/";
        }

        startDatabase(vemHost, Integer.parseInt(vemPort), vemRootURI, database, replica);
    }

    public static void stopDatabase(String vemHost, int vemPort, String vemUri, Database database) throws ActionException {
        if (logger.isDebugEnabled()) {
            logger.debug("going to stop database {} through http://{}:{}{}",
                    new Object[]{database.getName(), vemHost, vemPort, vemUri});
        }
        if (Constants.VDB_STATUS_OFFLINE.equals(database.getStatus())) {
            logger.warn("database {} is already stopped", database.getName());
            return;
        }

        if (!vemUri.endsWith("/")) {
            vemUri = vemUri + "/";
        }

        RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "mgmt/databases/" + database.getId()
                + "/stop", Constants.HTTP_METHOD_PUT, null);
        //try to monitor database status

        long start = System.currentTimeMillis();
        long timeout = 600 * 1000;
        String timeoutStr = GRMConfigManager.getLocalConfigValue(Constants.CFG_GRL_VOLT_DB_START_TIMEOUT);
        if (StringUtils.isNotEmpty(timeoutStr)) {
            timeout = Integer.parseInt(timeoutStr) * 1000L;
        }
        if (logger.isTraceEnabled()) {
            logger.trace("waiting for voltdb stopping for {} ms", timeout);
        }
        while (true) {
            Database db = RESTfulUtils.getVoltDBDatabase(database.getName(), false/*not calling stored procedure to check replica status*/);
            if (Constants.VDB_STATUS_OFFLINE.equals(db.getStatus())) {
                logger.debug("database {} has been stopped", db.getName());
                break;
            }
            if (System.currentTimeMillis() - start >= timeout) {
                logger.error("waiting for voltdb database {} stopping for {} ms", db.getName(), System.currentTimeMillis() - start);
                throw new ActionException("waiting for voltdb database stopping expired");
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
                break;
            }
        }
    }

    public static void stopDatabase(Database database) throws ActionException {
        String voltDBNodePath = Constants.CFG_GRG_ROOT + "/" + GRMConfigManager.getSiteName() + "/" + Constants.CFG_GRG_VOLTDB;

        String vemHost = Utils.getVEMHostIP(GRMConfigManager.getSiteName());
        String vemPort = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_PORT);
        String vemRootURI = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_URI);

        if (!vemRootURI.endsWith("/")) {
            vemRootURI = vemRootURI + "/";
        }

        stopDatabase(vemHost, Integer.parseInt(vemPort), vemRootURI, database);
    }

    public static void recoverDatabase(String vemHost, int vemPort, String vemUri, Database database) throws ActionException {
        if (logger.isDebugEnabled()) {
            logger.debug("going to recover database {} through http://{}:{}{}",
                    new Object[]{database.getName(), vemHost, vemPort, vemUri});
        }
        if (!Constants.VDB_STATUS_OFFLINE.equals(database.getStatus())) {
            logger.error("database {} is in wrong status {}", database.getName(), database.getStatus());
            throw new DatabaseException("Illegal database status");
        }

        if (!vemUri.endsWith("/")) {
            vemUri = vemUri + "/";
        }

        RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "mgmt/databases/" + database.getId()
                + "/recover", Constants.HTTP_METHOD_PUT, null);
        //try to monitor database status

        long start = System.currentTimeMillis();
        long timeout = 600 * 1000;
        String timeoutStr = GRMConfigManager.getLocalConfigValue(Constants.CFG_GRL_VOLT_DB_START_TIMEOUT);
        if (StringUtils.isNotEmpty(timeoutStr)) {
            timeout = Integer.parseInt(timeoutStr) * 1000L;
        }
        if (logger.isTraceEnabled()) {
            logger.trace("waiting for voltdb recovering for {} ms", timeout);
        }
        while (true) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
                break;
            }
            Database db = RESTfulUtils.getVoltDBDatabase(database.getName());
            if (Constants.VDB_STATUS_ONLINE.equals(db.getStatus())) {
                logger.debug("database {} has been started", db.getName());
                break;
            }
            if (System.currentTimeMillis() - start >= timeout) {
                logger.error("waiting for voltdb database {} starting for {} ms", db.getName(), System.currentTimeMillis() - start);
                throw new ActionException("waiting for voltdb database recovering expired");
            }
        }
    }

    public static void recoverDatabase(Database database) throws ActionException {
        String voltDBNodePath = Constants.CFG_GRG_ROOT + "/" + GRMConfigManager.getSiteName() + "/" + Constants.CFG_GRG_VOLTDB;

        String vemHost = Utils.getVEMHostIP(GRMConfigManager.getSiteName());
        String vemPort = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_PORT);
        String vemRootURI = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_URI);

        if (!vemRootURI.endsWith("/")) {
            vemRootURI = vemRootURI + "/";
        }

        recoverDatabase(vemHost, Integer.parseInt(vemPort), vemRootURI, database);
    }

    public static void pauseDatabase(String vemHost, int vemPort, String vemUri, Database database) throws ActionException {
        if (logger.isDebugEnabled()) {
            logger.debug("going to pause database {} through http://{}:{}{}",
                    new Object[]{database.getName(), vemHost, vemPort, vemUri});
        }
        if (!Constants.VDB_STATUS_ONLINE.equals(database.getStatus())) {
            logger.error("database {} is in wrong status {}", database.getName(), database.getStatus());
            throw new DatabaseException("Illegal database status");
        }

        if (!vemUri.endsWith("/")) {
            vemUri = vemUri + "/";
        }

        RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "mgmt/databases/" + database.getId()
                + "/pause", Constants.HTTP_METHOD_PUT, null);

        //try to monitor database status

        long start = System.currentTimeMillis();
        long timeout = 600 * 1000;
        String timeoutStr = GRMConfigManager.getLocalConfigValue(Constants.CFG_GRL_VOLT_DB_START_TIMEOUT);
        if (StringUtils.isNotEmpty(timeoutStr)) {
            timeout = Integer.parseInt(timeoutStr) * 1000L;
        }
        if (logger.isTraceEnabled()) {
            logger.trace("waiting for voltdb pause for {} ms", timeout);
        }
        while (true) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
                break;
            }
            Database db = RESTfulUtils.getVoltDBDatabase(database.getName());
            if (Constants.VDB_STATUS_PAUSED.equals(db.getStatus())) {
                logger.debug("database {} has been paused", db.getName());
                break;
            }
            if (System.currentTimeMillis() - start >= timeout) {
                logger.error("waiting for voltdb database {} pausing for {} ms", db.getName(), System.currentTimeMillis() - start);
                throw new ActionException("waiting for voltdb database pausing expired");
            }
        }
    }

    public static void pauseDatabase(Database database) throws ActionException {
        String voltDBNodePath = Constants.CFG_GRG_ROOT + "/" + GRMConfigManager.getSiteName() + "/" + Constants.CFG_GRG_VOLTDB;

        String vemHost = Utils.getVEMHostIP(GRMConfigManager.getSiteName());
        String vemPort = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_PORT);
        String vemRootURI = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_URI);

        if (!vemRootURI.endsWith("/")) {
            vemRootURI = vemRootURI + "/";
        }

        pauseDatabase(vemHost, Integer.parseInt(vemPort), vemRootURI, database);
    }

    public static void resumeDatabase(String vemHost, int vemPort, String vemUri, Database database) throws ActionException {
        if (logger.isDebugEnabled()) {
            logger.debug("going to resume database {} through http://{}:{}{}",
                    new Object[]{database.getName(), vemHost, vemPort, vemUri});
        }
        if (!Constants.VDB_STATUS_PAUSED.equals(database.getStatus())) {
            logger.error("database {} is in wrong status {}", database.getName(), database.getStatus());
            throw new DatabaseException("Illegal database status");
        }

        if (!vemUri.endsWith("/")) {
            vemUri = vemUri + "/";
        }

        RESTfulUtils.getRESTfulInvokeResult(vemHost, vemPort, vemUri + "mgmt/databases/" + database.getId()
                + "/resume", Constants.HTTP_METHOD_PUT, null);

        //try to monitor database status

        long start = System.currentTimeMillis();
        long timeout = 600 * 1000;
        String timeoutStr = GRMConfigManager.getLocalConfigValue(Constants.CFG_GRL_VOLT_DB_START_TIMEOUT);
        if (StringUtils.isNotEmpty(timeoutStr)) {
            timeout = Integer.parseInt(timeoutStr) * 1000L;
        }
        if (logger.isTraceEnabled()) {
            logger.trace("waiting for voltdb pause for {} ms", timeout);
        }
        while (true) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
                break;
            }
            Database db = RESTfulUtils.getVoltDBDatabase(database.getName());
            if (Constants.VDB_STATUS_ONLINE.equals(db.getStatus())) {
                logger.debug("database {} has been resumed", db.getName());
                break;
            }
            if (System.currentTimeMillis() - start >= timeout) {
                logger.error("waiting for voltdb database {} resuming for {} ms", db.getName(), System.currentTimeMillis() - start);
                throw new ActionException("waiting for voltdb database resuming expired");
            }
        }
    }

    public static void resumeDatabase(Database database) throws ActionException {
        String voltDBNodePath = Constants.CFG_GRG_ROOT + "/" + GRMConfigManager.getSiteName() + "/" + Constants.CFG_GRG_VOLTDB;

        String vemHost = Utils.getVEMHostIP(GRMConfigManager.getSiteName());
        String vemPort = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_PORT);
        String vemRootURI = Utils.getConfigurationValue(voltDBNodePath, Constants.CFG_GRG_VEM_URI);

        if (!vemRootURI.endsWith("/")) {
            vemRootURI = vemRootURI + "/";
        }

        resumeDatabase(vemHost, Integer.parseInt(vemPort), vemRootURI, database);
    }
}
